home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1993 / MacHack 1993.toast / MacHack™ 1987-1992 / MacHack™ '90 / Source Code ƒ / MPW C ƒ / NetTimeProtocol ƒ / ntptime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-03-23  |  13.8 KB  |  612 lines  |  [TEXT/MPS ]

  1. #ifndef lint
  2. static char *RCSid = "$Header: ntptime.c,v 1.10 89/02/19 16:08:56 bww Exp $";
  3. #endif
  4.  
  5. #define VERSIONSTRING "ntptime v 1.12"
  6. /*
  7.  * ntptime - get/set the time via ntp
  8.  *
  9.  ****************************************************************
  10.  * HISTORY
  11.  * $Log:    ntptime.c,v $
  12.  * Revision 1.10  89/02/19  16:08:56  bww
  13.  *     SSP headers.  Added "-t" flag.
  14.  *     [89/02/19  16:07:44  bww]
  15.  * 
  16.  * Revision 1.9  89/02/09  18:30:36  bww
  17.  * Turn exit code 1 into 2 to avoid "not found" territory.
  18.  * 
  19.  * Revision 1.8  88/11/26  16:26:05  bww
  20.  * Don't set the time if 1/3 of the hosts disagree and the change is big.
  21.  * 
  22.  * Revision 1.7  88/11/11  16:07:18  bww
  23.  * Upgraded to latest UMD release.
  24.  * 
  25.  * Revision 1.6  88/09/09  19:33:40  bww
  26.  * Use filter index zero first.
  27.  * 
  28.  * Revision 1.5  88/08/25  14:52:50  bww
  29.  * Exit codes [0 .. 10] give (decreasing) confidence in result.
  30.  * 
  31.  * Revision 1.4  88/08/22  17:17:05  bww
  32.  * Bumped size of receive buffer.
  33.  * 
  34.  * Revision 1.3  88/08/20  17:00:23  bww
  35.  * Fixed interrupt handling and added timeouts.  Also added
  36.  * beginnings of a sanity check before setting the time.
  37.  * 
  38.  * Revision 1.2  88/08/16  19:11:09  bww
  39.  * Added definition of "debug".
  40.  * 
  41.  * Revision 1.1  88/07/25  23:32:19  bww
  42.  * Initial revision
  43.  * 
  44.  */
  45.  
  46. #include    <Types.h>
  47. #include    <Events.h>
  48. #include    <Math.h>
  49. #include     <fcntl.h>
  50. #include     <stdio.h>
  51. #include    <ErrMgr.h>
  52. #include    <CursorCtl.h>
  53. #include    <Errors.h>
  54. #include    <Devices.h>
  55. #include    <MacTCPCommonTypes.h>
  56. #include    <UDPPB.h>
  57. #include    <TCPPB.h>
  58. #include     <GetMyIPAddr.h>
  59. #include    <AddressXlation.h>
  60. #include    <Time.h>
  61. #include     "ntp.h"
  62.  
  63. /*
  64.  * exit codes:
  65.  *    EX_OK                        : all ok
  66.  *    EX_OK + [1 .. EX__RANGE]    : unreliable range - time set
  67.  *    EX_OK + EX__RANGE + 1        : unreliable - time not set
  68.  *    EX_OK + EX__RANGE + 2        : unreliabe - didn't converge (timer expired)
  69.  *    EX__SIGBASE + s                : interrupted with signal s
  70.  *    others                        : <sysexits.h> codes
  71.  */
  72. #define EX_OK 0
  73. #define EX__RANGE    10
  74. #define EX__SIGBASE    32
  75.  
  76. #define EX_OSERR    11
  77. #define EX_OSFILE    12
  78. #define EX_USAGE    13
  79.  
  80. #define DEFTOWAIT    15    /* # seconds */
  81.  
  82. #define NTPD_PORT 123  /* 123/udp */
  83.  
  84. #define NIL 0
  85. #define TICKPERSECOND 60
  86.  
  87. static char *name;
  88. static StreamPtr myudpstream    = 0;
  89. static udp_port myudpport = 0;
  90. static int doset        = 0;
  91. static int towait        = DEFTOWAIT;
  92. static int verbose        = 0;
  93. static char conffile[]        = NTPINITFILE;
  94. static struct ntp_peer *peers    = 0;
  95. static short refnum = 0;
  96.  
  97. struct ntp_peer *confinit();
  98. void setthetime();
  99. void chkserver();
  100. void usage();
  101.  
  102. extern int errno;
  103. #ifdef DEBUG
  104. int debug;
  105. #endif
  106.  
  107. extern char *malloc();
  108. extern char *ctime();
  109. extern double ul_fixed_to_double();
  110. extern double minimum_filter();
  111. extern void ntpsend(struct ntp_peer *peer);
  112. extern void packet();
  113. extern void setminuteseast(long num);
  114. extern char * macctime(const time_t *unixsecs);
  115.  
  116. void bzero(char * bytes, int len)
  117. {
  118.     register char *p;
  119.  
  120.     for (p = bytes; len > 0; len--,p++) {
  121.         *p = '\0';
  122.     }
  123. }
  124.  
  125. char *inet_ntoa(ip_addr addr)
  126. {
  127.     static char buf[100];
  128.  
  129.     if (AddrToStr(addr,buf) != noErr) {
  130.         return "?use resolver?";
  131.     } else {
  132.         return buf;
  133.     }
  134. }
  135.  
  136. void clean_exit(int code)
  137. {
  138.     UDPiopb udppb;
  139.  
  140.     if (myudpstream != 0) {
  141.         bzero((char *)&udppb,sizeof(udppb));
  142.         udppb.ioCompletion    = NIL;
  143.         udppb.ioCRefNum        = refnum;
  144.         udppb.csCode         = UDPRelease;
  145.         udppb.udpStream     = myudpstream;
  146.         udppb.csParam.create.userDataPtr    = NIL;
  147.         
  148.     
  149.         if (PBControl((ParmBlkPtr)&udppb,false) != noErr) {
  150.             fprintf(stderr,"Trouble releasing udp stream in clean_exit(%d)\n",code);
  151.         }
  152.     }
  153.     (void)CloseResolver(); /* ignore errors */
  154.     if (code != EX_OK) {
  155.         fprintf(stderr,"clean_exit(%d)\n",code);
  156.     }
  157.     exit(code);
  158. }
  159.  
  160. void     FailOSErr(msg, err)
  161.     char    *msg;
  162.     OSErr    err;
  163. {
  164.     if (err != noErr) {
  165.         fprintf(stderr,"%s errcode=%d\n",msg,err);
  166.         clean_exit (-1);
  167.     }
  168. }
  169.  
  170. void     ReportOSErr(msg, err)
  171.     char    *msg;
  172.     OSErr    err;
  173. {
  174.     if (err != noErr) {
  175.         fprintf(stderr,"%s errcode=%d\n",msg,err);
  176.     }
  177. }
  178.  
  179. void
  180. waitforonesecond()
  181. {
  182.     register unsigned long ticks;
  183.     for (ticks = TickCount(); TickCount() < (ticks + TICKPERSECOND);) {
  184.         EventRecord theEvent;
  185.     
  186.         (void)EventAvail(0,&theEvent);
  187.         packet(); /* checks for incoming packets */
  188.     }
  189. }
  190.  
  191. void
  192. main(ac, av)
  193.     int ac;
  194.     char **av;
  195. {
  196.     register char *p;
  197.     register struct ntp_peer *thispeer;
  198.     UDPiopb udppb;
  199.     unsigned long ticks;
  200.  
  201.     InitCursorCtl(nil);
  202.     name = (ac > 0) ? (ac--, *av++) : (ac = 0, "ntptime");
  203.     while (ac && *(p = *av) == '-') {
  204.         while (*++p)
  205.             switch (*p) {
  206.             case 's':
  207.                 doset = 1;
  208.                 break;
  209.             case 't':
  210.                 if (*(p+1) == 0) {
  211.                     if (ac == 1)
  212.                         usage();
  213.                     towait = atoi((--ac, *++av));
  214.                 } else if (*++p < '0' || '9' < *p)
  215.                     usage();
  216.                 else {
  217.                     towait = atoi(p);
  218.                     while ('0' <= *++p && *p <= '9')
  219.                         continue;
  220.                     --p;
  221.                 }
  222.                 break;
  223.             case 'v':
  224.                 verbose++;
  225.                 break;
  226.             case '?':
  227.             default:
  228.                 usage();
  229.             }
  230.         ac--, av++;
  231.     }
  232.     if (ac)
  233.         usage();
  234.  
  235.     if (verbose) {
  236.         fprintf(stdout,"[ %s ]\n",VERSIONSTRING);
  237.         (void) fflush(stdout);
  238.     }
  239.     FailOSErr("OpenResolver", OpenResolver(NIL));
  240.     FailOSErr("Can't open driver",opendriver(".IPP",&refnum));
  241.  
  242.     /* grab a udp port, to use for sending packets.  NoDelay, Async */
  243.  
  244.     p = malloc(4096);
  245.     if (p == NULL) {
  246.         fprintf(stderr,"malloc fails\n");
  247.         clean_exit(-1);
  248.     }
  249.     bzero((char *)&udppb,sizeof(udppb));
  250.     udppb.ioCompletion    = NIL;
  251.     udppb.ioCRefNum        = refnum;
  252.     udppb.csCode         = UDPCreate;
  253.     udppb.csParam.create.rcvBuff         = p;
  254.     udppb.csParam.create.rcvBuffLen        = 4096;
  255.     udppb.csParam.create.notifyProc        = NIL;
  256.     udppb.csParam.create.localPort        = 0;
  257.     udppb.csParam.create.userDataPtr    = 0;
  258.  
  259.     FailOSErr("UDPCreate Fails",PBControl((ParmBlkPtr)&udppb,false));
  260.  
  261.     myudpstream = udppb.udpStream;
  262.     myudpport = udppb.csParam.create.localPort;
  263.  
  264.     /* check if the server is running */
  265.     if (doset)
  266.         chkserver(NTPD_PORT);
  267.  
  268.     peers = confinit(NTPD_PORT);
  269.  
  270.     /* now, every second, pool everyone */
  271.     for (ticks = TickCount(); TickCount() < ticks + (towait * TICKPERSECOND); waitforonesecond()) {
  272.         for (thispeer = peers; thispeer; thispeer = thispeer->next_peer) {
  273.             if (++thispeer->timer >= NTP_GRANPOLL) {
  274.                 ntpsend(thispeer);
  275.             }
  276.             packet(); /* checks for incoming packets */
  277.         }
  278.     }
  279.     if (doset) {
  280.         /* we shouldn't get here if doset is true */
  281.         fprintf(stderr, "%s: not enough time to converge - clock not set\n", name);
  282.         clean_exit(EX_OK + EX__RANGE + 2);
  283.     }
  284.     clean_exit(EX_OK);
  285. }
  286.  
  287.  
  288. void
  289. ntpsend(peer)
  290.     register struct ntp_peer *peer;
  291. {
  292.     struct ntpdata pkt;
  293.     struct timeval tp;
  294.     UDPiopb udppb;
  295.     wdsEntry wds[2];
  296.     OSErr    err;
  297.  
  298.     SpinCursor(8);
  299.     
  300.     bzero((char *) &pkt, sizeof(pkt));
  301.     pkt.status = NTPVERSION_1;
  302.     pkt.stratum = UNSPECIFIED;
  303.     pkt.poll = 0;
  304.     pkt.org.int_part = pkt.org.fraction = 0;
  305.     pkt.rec.int_part = pkt.rec.fraction = 0;
  306.     if (gettimeofday(&tp, (struct timezone *) 0) == -1) {
  307.         perror(name);
  308.         return;
  309.     }
  310.     tstamp(&pkt.xmt, &tp);
  311.  
  312.     bzero((char *)&udppb,sizeof(udppb));
  313.     udppb.ioCompletion    = NIL;
  314.     udppb.ioCRefNum        = refnum;
  315.     udppb.csCode         = UDPWrite;
  316.     udppb.udpStream     = myudpstream;
  317.     udppb.csParam.send.remoteHost      = peer->src;
  318.     udppb.csParam.send.remotePort    = peer->port;
  319.     udppb.csParam.send.userDataPtr    = NIL;
  320.  
  321.     /* build write-data structure */
  322.     wds[0].length    = sizeof(pkt);
  323.     wds[0].ptr        = (char *) &pkt;
  324.     wds[1].length    = 0;
  325.     
  326.     udppb.csParam.send.wdsPtr    = (Ptr)&wds;
  327.  
  328.  
  329.     err = PBControl((ParmBlkPtr)&udppb,false);
  330.     if (err == ipDestDeadErr) { /* are there other codes here we should special case? */
  331.         fprintf(stderr,"Host %s not responding, errcode=%d\n",inet_ntoa(peer->src),err);
  332.     } else if (err != noErr) {
  333.         fprintf(stderr,"UDPsend fails errcode=%d\n",err);
  334.     }
  335.     
  336.     peer->timer = 0;
  337.     peer->pkt_sent++;
  338. }
  339.  
  340. void
  341. packet()
  342. {
  343.     register struct ntp_peer *p;
  344.     register int i;
  345.     struct ntpdata pkt;
  346.     struct timeval tp;
  347.     double t1, t2, t3, t4;
  348.     double offset, delay;
  349.     double t_offset[SAMPLES], t_delay[SAMPLES];
  350.     int len;
  351.     UDPiopb udppb;
  352.     OSErr    err;
  353.  
  354.     SpinCursor(8);
  355.     
  356.     bzero((char *)&udppb,sizeof(udppb));
  357.     udppb.ioCompletion    = NIL;
  358.     udppb.ioCRefNum        = refnum;
  359.     udppb.csCode         = UDPRead;
  360.     udppb.udpStream     = myudpstream;
  361.     udppb.csParam.receive.timeOut              = 2; /* minimum time */
  362.     udppb.csParam.receive.secondTimeStamp    = 0;
  363.     udppb.csParam.receive.userDataPtr        = NIL;
  364.     err = PBControl((ParmBlkPtr)&udppb,false);
  365.     
  366.     if (err == commandTimeout) {
  367.         fprintf(stderr,"packet:UDPRead times out\n");
  368.         return;
  369.     } else if (err != noErr) {
  370.         fprintf(stderr,"packet:UDPRead fails\n");
  371.         return;
  372.     }
  373.  
  374.     /* copy bytes into pkt */
  375.     len = udppb.csParam.receive.rcvBuffLen;
  376.     
  377.     memcpy (&pkt, udppb.csParam.receive.rcvBuff, 
  378.             (sizeof(pkt) > len) ? (len) : (sizeof(pkt)));
  379.     
  380.     /* release buffer space */
  381.     udppb.csCode         = UDPBfrReturn;
  382.     if (PBControl((ParmBlkPtr)&udppb,false) != noErr) {
  383.         fprintf(stderr,"packet:UDPBfrReturn fails\n");
  384.     }
  385.     if (sizeof(pkt) != len) {
  386.         fprintf(stderr,"packet was %d bytes, expected %d\n",len,sizeof(pkt));
  387.     }
  388.     
  389.     if (gettimeofday(&tp, (struct timezone *) 0) == -1) {
  390.         perror(name);
  391.         return;
  392.     }
  393.     if (pkt.stratum == UNSPECIFIED || pkt.stratum > 8
  394.     || (pkt.status & LEAPMASK) == ALARM)
  395.         return;
  396.  
  397.     for (p = peers; p; p = p->next_peer)
  398.         if (p->src == udppb.csParam.receive.remoteHost)
  399.             break;
  400.     if (p == 0 || udppb.csParam.receive.remotePort != p->port)
  401.         return;
  402.  
  403.     p->pkt_rcvd++;
  404.     tstamp(&p->rec, &tp);
  405.  
  406.     if ((pkt.org.int_part == 0 && pkt.org.fraction == 0)
  407.     || (pkt.rec.int_part == 0 && pkt.rec.fraction == 0))
  408.         return;
  409.  
  410.     t1 = ul_fixed_to_double(&pkt.org);
  411.     t2 = ul_fixed_to_double(&pkt.rec);
  412.     t3 = ul_fixed_to_double(&pkt.xmt);
  413.     t4 = ul_fixed_to_double(&p->rec);
  414.     offset = ((t2 - t1) + (t3 - t4)) / 2.0;
  415.     delay = (t2 - t1) - (t3 - t4);
  416.  
  417.     i = p->filter.index++ % SAMPLES;
  418.     p->filter.offset[i] = offset;
  419.     p->filter.delay[i] = delay;
  420.  
  421.     for (i = 0; i < SAMPLES; i++) {
  422.         t_delay[i] = p->filter.delay[i];
  423.         t_offset[i] = p->filter.offset[i];
  424.     }
  425.     p->dispersion = minimum_filter(t_delay, t_offset, SAMPLES);
  426.     p->delay = t_delay[0];
  427.     p->offset = t_offset[0];
  428.  
  429.     if (p->dispersion <= PEER_THRESHOLD)
  430.         setthetime(p);
  431.  
  432.     ntpsend(p);
  433. }
  434.  
  435. int
  436. sanitychk(best)
  437.     register struct ntp_peer *best;
  438. {
  439.     register struct ntp_peer *p;
  440.     register int cnt = 0, bad = 0;
  441.  
  442.     for (p = peers; p; p = p->next_peer) {
  443.         if (p == best || p->pkt_rcvd == 0)
  444.             continue;
  445.         cnt++;
  446.         if (fabs(best->offset - p->offset) < WAYTOOBIG/2)
  447.             continue;
  448.         fprintf(stdout, "Warning: %s differs by %f seconds\n",
  449.                     inet_ntoa(p->src),
  450.                     best->offset - p->offset);
  451.         bad++;
  452.     }
  453.     if (cnt == 0 || bad == 0)
  454.         return 0;
  455.     return (bad * EX__RANGE) / cnt;
  456. }
  457.  
  458. void
  459. setthetime(best)
  460.     register struct ntp_peer *best;
  461. {
  462.     register struct ntp_peer *p;
  463.     register int code;
  464.     double offset;
  465.     struct timeval tv;
  466.  
  467.     if (verbose)
  468.         for (p = peers; p; p = p->next_peer)
  469.             fprintf(stdout, "[ %16s%c %lu/%lu (%f/%f) ]\n",
  470.                     inet_ntoa(p->src),
  471.                     p == best ? '*' : ' ',
  472.                     p->pkt_sent, p->pkt_rcvd,
  473.                     p->dispersion, p->offset);
  474.     offset = best->offset;
  475.     if (doset == 0) {
  476.         fprintf(stdout, "Your clock is running %.3f seconds ", fabs(offset));
  477.         fprintf(stdout, "(%.3f minutes) %s\n",
  478.                 fabs(offset) / 60.0, (offset<0.0) ? "fast" : "slow");
  479.         clean_exit(EX_OK);
  480.     }
  481.     code = sanitychk(best);
  482.     if (code > 3 && fabs(offset) > WAYTOOBIG) {
  483.         fprintf(stderr, "%s: unreliable time - clock not set\n", name);
  484.         clean_exit(EX_OK + EX__RANGE + 1);
  485.     }
  486.     if (gettimeofday(&tv, (struct timezone *) 0) == -1) {
  487.         perror(name);
  488.         clean_exit(EX_OSERR);
  489.     }
  490.     offset += tv.tv_sec;
  491.     offset += tv.tv_usec / 1000000.0;
  492.     tv.tv_sec = offset;
  493.     tv.tv_usec = (offset - tv.tv_sec) * 1000000;
  494.     if (settimeofday(&tv, (struct timezone *) 0) == -1) {
  495.         fprintf(stderr, "%s: can't set the time\n", name);
  496.     }
  497.     fputs(macctime(&tv.tv_sec), stdout);
  498.     if (code)
  499.         fprintf(stdout, "*** CHECK THE DATE ***\n");
  500.     clean_exit(EX_OK + code);
  501. }
  502.  
  503. struct ntp_peer *
  504. confinit(port)
  505.     ip_port port;
  506. {
  507.     register struct ntp_peer *p, *q;
  508.     register FILE *f;
  509.     register int i;
  510.     char buf[BUFSIZ], word[BUFSIZ];
  511.     ip_addr src;
  512.     struct hostInfo hp;
  513.     long numminuteseast = 0;
  514.  
  515.     p = NIL;
  516.     if ((f = fopen(conffile, "r")) == NULL) {
  517.         perror(conffile);
  518.         clean_exit(EX_OSFILE);
  519.     }
  520.     while (fgets(buf, sizeof(buf), f) != NULL) {
  521.         if (sscanf(buf, "%s", word) != 1) 
  522.             continue;
  523.         if (strcmp(word, "peer") == 0) {
  524.             if (sscanf(buf, "%*s %s", word) != 1) {
  525.                 fprintf(stderr, "%s: bad peer line\n", name);
  526.                 clean_exit(EX_OSFILE);
  527.             }
  528.             if (StrToAddr(word,&hp,NIL,NIL) != noErr) {
  529.                 fprintf(stderr, "%s: unknown host\n", word);
  530.                 clean_exit(EX_OSFILE);
  531.             }
  532.             src = hp.addr[0];
  533.             for (q = p; q; q = q->next_peer)
  534.                 if (q->src == src)
  535.                     break;
  536.             if (q)
  537.                 continue;
  538.             if ((q = (struct ntp_peer *) malloc(sizeof(*q))) == 0) {
  539.                 perror(name);
  540.                 clean_exit(EX_OSERR);
  541.             }
  542.             q->src = src;
  543.             q->port = port;
  544.             q->timer = NTP_GRANPOLL;
  545.             q->dispersion = NTP_MAXDISP;
  546.             q->offset = 0.0;
  547.             q->delay = 0.0;
  548.             q->filter.index = 0;
  549.             for (i = 0; i < SAMPLES; i++) {
  550.                 q->filter.offset[i] = 0.0;
  551.                 q->filter.delay[i] = NTP_MAXDELAY;
  552.             }
  553.             q->pkt_sent = 0;
  554.             q->pkt_rcvd = 0;
  555.             q->next_peer = p;
  556.             p = q;
  557.         } else if (strcmp(word, "minuteseast") == 0) {
  558.             if (sscanf(buf, "%*s %d", &numminuteseast) != 1) {
  559.                 fprintf(stderr, "%s: bad minuteseast line\n", name);
  560.                 clean_exit(EX_OSFILE);
  561.             }
  562.         }
  563.     }
  564.     setminuteseast(numminuteseast);
  565.     (void) fclose(f);
  566.     return p;
  567. }
  568.  
  569.  
  570. void
  571. chkserver(port)
  572.     int port;
  573. {
  574.  
  575.     UDPiopb udppb;
  576.     char dummy[2048];
  577.  
  578.     /* grab a udp port, to use for sending packets.  NoDelay, Async */
  579.  
  580.     bzero((char *)&udppb,sizeof(udppb));
  581.     udppb.ioCompletion    = NIL;
  582.     udppb.ioCRefNum        = refnum;
  583.     udppb.csCode         = UDPCreate;
  584.     udppb.csParam.create.rcvBuff         = &dummy;
  585.     udppb.csParam.create.rcvBuffLen        = 2048;
  586.     udppb.csParam.create.notifyProc        = NIL; 
  587.     udppb.csParam.create.localPort        = port;
  588.     udppb.csParam.create.userDataPtr    = NIL;
  589.  
  590.     if (PBControl((ParmBlkPtr)&udppb,false) == noErr) {
  591.         udppb.ioCompletion    = NIL;
  592.         udppb.ioCRefNum        = refnum;
  593.         udppb.csCode         = UDPRelease;
  594.         /* leave stream pointer alone */
  595.         udppb.csParam.create.userDataPtr    = NIL;
  596.         
  597.         if (PBControl((ParmBlkPtr)&udppb,false) != noErr) {
  598.             fprintf(stderr,"Trouble releasing test stream in chkserver()\n");
  599.         }
  600.     } else {
  601.         fprintf(stderr, "Warning: ntp server active\n");
  602.     }
  603. }
  604.  
  605.  
  606. void
  607. usage()
  608. {
  609.     fprintf(stderr, "Usage: %s [-v] [-s] [-t secs]\n", name);
  610.     clean_exit(EX_USAGE);
  611. }
  612.